#include "sGet.h"

#include <Poco/DOM/Document.h>
#include <Poco/DOM/NodeIterator.h>
#include <Poco/DOM/NamedNodeMap.h>
#include <Poco/Net/DatagramSocket.h>
#include <Poco/Net/SocketAddress.h>
#include <Poco/Base64Encoder.h>
#include <Poco/Base64Decoder.h>
#include <Poco/StreamCopier.h>
#include <Poco/DigestStream.h>
#include <Poco/SHA1Engine.h>
#include "misc/BigInt/BigIntegerLibrary.hh"
#include "server/net/NetworkControl.h"
#include "server/net/sData.h"
#include "misc/Interface.h"
#include "server/file/FileControl.h"
#include <stdlib.h>
#include <sstream>
/*********************************************************/
void sGet::execute()
{
	Interface& iface = Interface::Instance();
	NetworkControl& nc = NetworkControl::Instance();
	FileControl& files = FileControl::Instance();

	// Send an ack
	ack();

	// We need to determine where the file should be stored
	// Hash the filename
	Poco::SHA1Engine		sha1;
	Poco::DigestOutputStream	ostr(sha1);
	ostr << fFileName;
	ostr.flush();
	std::string fHash = Poco::DigestEngine::digestToHex(sha1.digest());

	// Get the filename, local, predecessor and successor hash keys
	BigUnsigned localhash = BigUnsignedInABase(nc.getLocalHash(), 16);
	BigUnsigned succ_hash = BigUnsignedInABase(nc.getSuccessorHash(), 16);
	BigUnsigned pred_hash = BigUnsignedInABase(nc.getPredecessorHash(), 16);
	BigUnsigned file_hash = BigUnsignedInABase(fHash, 16);

	if (( localhash > file_hash ) && ( pred_hash < file_hash )) {
		iface << "Received download request from client: " << fHost << std::endl;
		iface.flush();
	} else if ( pred_hash > localhash ) {
		iface << "Received download request from client: " << fHost << std::endl;
		iface.flush();
	} else if (( pred_hash == succ_hash ) && ( localhash == pred_hash )) {
		iface << "Received download request from client: " << fHost << std::endl;
		iface.flush();
	} else {
		nc.sendTo(this, nc.getSuccessor());
		iface << "Forwarded download request to: " << nc.getSuccessor() << std::endl;
		iface.flush();

		ack();
		return;
	}

	// Is the file present in the repository?
	if ( !files.fileAvailable(fFileName)) {
		sData	dat;
		dat.setOrder(0);
		dat.setShell(true);
		dat.setFileName(fFileName);
		dat.setData("File not found on server");
		dat.setFileSize(0);
		nc.sendTo(&dat, fHost);
		iface << "Error opening file: ." << fFileName << "." << std::endl;
		iface.flush();
		return;
	}

	// Send all available chunks to the requesting client
	unsigned long nChunks = (unsigned long)((files.getFileSize(fFileName) / files.getChunkSize())) + 1;
	for ( unsigned long i = 0; i<nChunks; i++ ) {
		if ( files.chunkAvailable(fFileName, i) ) {
			std::string data = files.getChunk(fFileName, i);

			sData 	dat;
			dat.setOrder(i);
			dat.setShell(false);
			dat.setFileName(fFileName);
			dat.setData(data);
			dat.setFileSize(files.getFileSize(fFileName));

			nc.sendTo(&dat, fHost);
		}
	}

	iface << "Upload of file: " << fFileName << " complete." << std::endl;
	iface.flush();
}
/*********************************************************/
std::string sGet::serialize() const
{
	NetworkControl& ns = NetworkControl::Instance();
	std::ostringstream		xml;
	std::ostringstream		filestr;
	id = ns.getID();

	// The command needs to be a multiple of 6 bits ( due to base 64 encoding )
	std::string file_ = fFileName;
	while ( ((file_.length()*8) % 6) != 0 ) {
		file_.append("\n");
	}

	// We encode the command into a Base64 string so we can pass it without worrying about possible collisions with xml tags
	Poco::Base64Encoder	encoder(filestr);
	encoder << file_;

	// Build the xml string for this job
	id = ns.getID();
	xml << "<GS id='" << id << "'>";
	xml << "<Get>";
	xml << "<file>" << filestr.str() << "</file>";
	xml << "<host>" << fHost << "</host>";
	xml << "</Get>";
	xml << "</GS>";

	return( xml.str() );
}
/*********************************************************/
void sGet::deserialize(Poco::XML::NodeIterator& it)
{
	Poco::XML::Node* pNode = it.nextNode();

	if ( pNode->nodeName() != "file" ) {
		throw(std::logic_error("Node type get given, data field 'file' missing or incorrectly ordered!"));
	} else {
		// Decode the filename from a Base64 representation
		std::ostringstream	ostr;
		std::istringstream	istr;
	
		// Decode the result
		istr.str(pNode->innerText());			// Pass it into a inputstringstream
		Poco::Base64Decoder	decoder(istr);		// decode it...

		// Copy the decoded command to the local storage
		Poco::StreamCopier::copyStream(decoder, ostr);
		std::string result = ostr.str();

		// Remove any appended newlines to align for base 64 encoding
		for ( unsigned int i=0; i<result.length(); i++ ) {
			if ( result[i] == '\n' ) {
				result.erase(i,1);
				i = 0;	
			}
		}

		// Store the command
		fFileName = result;
	}

	pNode = it.nextNode();
	if ( pNode->nodeName() != "host" ) {
		throw(std::logic_error("Node type get given, data field 'host' missing or incorrectly ordered!"));
	} else {
		fHost = pNode->innerText();
	}
}
/*********************************************************/
std::string sGet::getHost() const
{
	return fHost;
}
/*********************************************************/
void sGet::setHost(const std::string& host)
{
	fHost = host;
}
/*********************************************************/
void sGet::setFileName(const std::string& file)
{
	fFileName = file;
}
/*********************************************************/
std::string sGet::getFileName() const
{
	return fFileName;
}
/*********************************************************/
sGet::sGet()
{
}
/*********************************************************/
sGet::~sGet()
{
}
/*********************************************************/
